/*
Scripts to create interactive tabs in SVG using ECMA script
Copyright (C) <2006>  <Andreas Neumann>
Version 1.2, 2006-04-03
neumann@karto.baug.ethz.ch
http://www.carto.net/
http://www.carto.net/neumann/

Credits:
* none so far

----

Documentation: http://www.carto.net/papers/svg/gui/tabgroup/

----

current version: 1.2.1

version history:
1.0 (2006-03-11)
initial version

1.1 (2006-04-03)
added ".moveTo(x,y)" and ".resize(width,height)" methods, added an additional g-element to hold transform values

1.2 (2006-06-19)
this.parentNode can now also be of type node reference (g or svg element); fixed a small bug when "hideContent" was set to true; changed this.parentGroup and introduced this.tabGroup
introduced id for group where content can be added, id name is: this.id+"__"+i+"_content"

1.2.1 (2006-07-07)
fixed a bug for multiline tabs (dy attribute of the tab texts). This bug was apparent when having more than two lines of text in the tabs

-------


This ECMA script library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library (lesser_gpl.txt); if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

----

original document site: http://www.carto.net/papers/svg/gui/tabgroup/
Please contact the author in case you want to use code or ideas commercially.
If you use this code, please include this copyright header, the included full
LGPL 2.1 text and read the terms provided in the LGPL 2.1 license
(http://www.gnu.org/copyleft/lesser.txt)

-------------------------------

Please report bugs and send improvements to neumann@karto.baug.ethz.ch
If you use this control, please link to the original (http://www.carto.net/papers/svg/gui/tabgroup/)
somewhere in the source-code-comment or the "about" of your project and give credits, thanks!

*/

function tabgroup(id,parentNode,transx,transy,width,height,tabheight,cornerLeft,cornerRight,tabmargins,spaceBetweenTabs,tabStyles,activetabBGColor,tabwindowStyles,tabtextStyles,tabTitles,activeTabindex,hideContent,functionToCall) {
	var nrArguments = 19;
	var createTabgroup = true;
	if (arguments.length == nrArguments) {
		this.id = id;
		this.parentNode = parentNode; //can be of type string (id) or node reference (svg or g node)
		this.transx = transx;
		this.transy = transy;
		this.x = 0;
		this.y = 0;
		this.width = width;
		this.height = height;
		this.tabheight = tabheight;
		this.cornerLeft = cornerLeft; //values are "rect","round","triangle"
		this.cornerRight = cornerRight; //values are "rect","round","triangle"
		this.tabmargins = tabmargins;
		this.spaceBetweenTabs = spaceBetweenTabs;
		this.tabStyles = tabStyles;
		if (!this.tabStyles["fill"]) {
			this.tabStyles["fill"] = "lightgray";
		}
		this.activetabBGColor = activetabBGColor;
		this.tabwindowStyles = tabwindowStyles;
		this.tabtextStyles = tabtextStyles;
		if (!this.tabtextStyles["font-size"]) {
			this.tabtextStyles["font-size"] = 15;
		}
		this.tabTitles = tabTitles;
		if (this.tabTitles instanceof Array) {
			if (this.tabTitles.length == 0) {
				createTabgroup = false;
				alert("Error ("+this.id+"): the array 'tabTitles' has no elements!");
			}
		}
		else {
			createTabgroup = false;
			alert("Error ("+this.id+"): the array 'tabTitles' is not of type array!");
		}
		this.activeTabindex = activeTabindex;
		if (this.activeTabindex >= this.tabTitles.length) {
			createTabgroup = false;
			this.outOfBoundMessage(this.activeTabindex);
		}
		this.hideContent = hideContent; //boolean, defines whether the display of a tab should be set to "none","inherit"
		this.functionToCall = functionToCall;
		if (!(typeof(this.functionToCall) == "function" || typeof(this.functionToCall) == "object" || typeof(this.functionToCall) == "undefined")) {
			createTabgroup = false;
			alert("Error ("+this.id+"): functionToCall is of type '"+typeof(this.functionToCall)+"' and not of type 'function', 'object' or 'undefined'");
		}
	}
	else {
		createTabgroup = false;
		alert("Error ("+id+"): wrong nr of arguments! You have to pass over "+nrArguments+" parameters.");
	}
	if (createTabgroup) {
		this.parentGroup = null; //later a node reference to the parent group
		this.tabGroup = null; //later a reference to the group within the parentGroup
		this.tabwindows = new Array();
		this.timer = new Timer(this); //a Timer instance for calling the functionToCall
		this.timerMs = 200; //a constant of this object that is used in conjunction with the timer - functionToCall is called after 200 ms
		this.createTabGroup();
	}
	else {
		alert("Could not create tabgroup with id '"+this.id+"' due to errors in the constructor parameters");
	}
}

tabgroup.prototype.createTabGroup = function() {
	var result = this.testParent();
	if (result) {
	this.tabGroup = document.createElementNS(svgNS,"g");
	this.tabGroup.setAttributeNS(null,"transform","translate("+this.transx+","+this.transy+")");
	this.parentGroup.appendChild(this.tabGroup);
	//loop to create all tabs
	var currentX = this.x;
	for (var i=0;i<this.tabTitles.length;i++) {
		currentLeft = currentX;
		this.tabwindows[i] = new Array();
		//create group
		this.tabwindows[i]["group"] = document.createElementNS(svgNS,"g");
		this.tabGroup.appendChild(this.tabwindows[i]["group"]);
		//create tabTitle
		var tabtitles = this.tabTitles[i].split("\n");
		this.tabwindows[i]["tabTitle"] = document.createElementNS(svgNS,"text");
		this.tabwindows[i]["tabTitle"].setAttributeNS(null,"y",this.y + this.tabtextStyles["font-size"]);
		var value="";
		for (var attrib in this.tabtextStyles) {
			value = this.tabtextStyles[attrib];
			if (attrib == "font-size") {
				value += "px";
			}
			this.tabwindows[i]["tabTitle"].setAttributeNS(null,attrib,value);
		}
		this.tabwindows[i]["tabTitle"].setAttributeNS(null,"pointer-events","none");
		//create tspans and add text contents
		for (var j=0;j<tabtitles.length;j++) {
			var tspan = document.createElementNS(svgNS,"tspan");
			tspan.setAttributeNS(null,"x",currentX+this.tabmargins);
			var dy = this.tabtextStyles["font-size"]*1.1;
			if (j == 0) {
				dy = 0;
			}
			tspan.setAttributeNS(null,"dy",dy);
			var textNode = document.createTextNode(tabtitles[j]);
			tspan.appendChild(textNode);
			this.tabwindows[i]["tabTitle"].appendChild(tspan);
		}
		this.tabwindows[i]["group"].appendChild(this.tabwindows[i]["tabTitle"]);
		//get bbox
		var bbox = this.tabwindows[i]["tabTitle"].getBBox();
		currentX = Math.round(currentLeft + this.tabmargins * 2 + bbox.width);
		//check if text-anchor is middle and shift the x-values accordingly
		if (this.tabtextStyles["text-anchor"]) {
			if (this.tabtextStyles["text-anchor"] == "middle") {
				this.tabwindows[i]["tabTitle"].setAttributeNS(null,"transform","translate("+(bbox.width * 0.5)+" 0)");
			}
		}
		//now draw tabwindow background
		this.tabwindows[i]["tabbg"] = document.createElementNS(svgNS,"path");
		for (var attrib in this.tabwindowStyles) {
			this.tabwindows[i]["tabbg"].setAttributeNS(null,attrib,this.tabwindowStyles[attrib]);
		}
		//start the path for tab windows
		var d = "M";
		//left corner of tab
		if (this.cornerLeft == "rect") {
			d += currentLeft+" "+this.y;
		}
		if (this.cornerLeft == "triangle") {
			d += currentLeft+" "+(this.y+this.tabmargins)+"L"+(currentLeft+this.tabmargins)+" "+this.y;
		}
		if (this.cornerLeft == "round") {
			d += currentLeft+" "+(this.y+this.tabmargins)+"a"+this.tabmargins+" "+this.tabmargins+" 0 0 1 "+this.tabmargins+" -"+this.tabmargins;
		}
		//right corner of tab
		if (this.cornerRight == "rect") {
			d += "L"+currentX+" "+this.y;
		}
		if (this.cornerRight == "triangle") {
			d += "L"+(currentX-this.tabmargins)+" "+this.y+"L"+currentX+" "+(this.y+this.tabmargins);
		}
		if (this.cornerRight == "round") {
			d += "L"+(currentX-this.tabmargins)+" "+this.y+"a"+this.tabmargins+" "+this.tabmargins+" 0 0 1 "+this.tabmargins+" "+this.tabmargins;
		}
		d += "L"+currentX+" "+(this.y+this.tabheight);
		//complete the path for tab
		var dtab = d + "L"+currentLeft+" "+(this.y+this.tabheight)+"z";
		//complete the path for tab window
		d += "L"+(this.x+this.width)+" "+(this.y+this.tabheight)+"L"+(this.x+this.width)+" "+(this.y+this.height);
		d += "L"+this.x+" "+(this.y+this.height);
		if (currentLeft == this.x) {
			d += "z";
		}
		else {
			d += "L"+this.x+" "+(this.y+this.tabheight)+"L"+currentLeft+" "+(this.y+this.tabheight)+"z";
		}
		this.tabwindows[i]["tabbg"].setAttributeNS(null,"d",d);
		this.tabwindows[i]["group"].insertBefore(this.tabwindows[i]["tabbg"],this.tabwindows[i]["tabTitle"]);
		//create tab element
		this.tabwindows[i]["tab"] = document.createElementNS(svgNS,"path");
		for (var attrib in this.tabStyles) {
			this.tabwindows[i]["tab"].setAttributeNS(null,attrib,this.tabStyles[attrib]);
		}
		this.tabwindows[i]["tab"].setAttributeNS(null,"d",dtab);
		this.tabwindows[i]["tab"].setAttributeNS(null,"id",this.id+"__"+i);
		this.tabwindows[i]["tab"].addEventListener("click",this,false);
		this.tabwindows[i]["group"].insertBefore(this.tabwindows[i]["tab"],this.tabwindows[i]["tabTitle"]);
		//create group for tab content
		this.tabwindows[i]["content"] = document.createElementNS(svgNS,"g");
		this.tabwindows[i]["content"].setAttributeNS(null,"id",this.id+"__"+i+"_content");
		if (this.hideContent) {
			this.tabwindows[i]["content"].setAttributeNS(null,"display","none");
		}
		this.tabwindows[i]["group"].appendChild(this.tabwindows[i]["content"]);
		//set tab activate status
		this.tabwindows[i].activeStatus = true;
		//increment currentX with the space between tabs
		currentX += this.spaceBetweenTabs;
	}
	//activate one tab
	this.tabwindows[this.activeTabindex]["tab"].setAttributeNS(null,"fill",this.activetabBGColor);
	this.tabGroup.appendChild(this.tabwindows[this.activeTabindex]["group"]);
	if (this.hideContent) {
		this.tabwindows[this.activeTabindex]["content"].setAttributeNS(null,"display","inherit");
	}
	}
	else {
		alert("could not create or reference 'parentNode' of tabgroup with id '"+this.id+"'");
	}
 }
 
 //test if window group exists or create a new group at the end of the file
tabgroup.prototype.testParent = function() {
    //test if of type object
    var nodeValid = false;
    if (typeof(this.parentNode) == "object") {
    	if (this.parentNode.nodeName == "svg" || this.parentNode.nodeName == "g") {
    		this.parentGroup = this.parentNode;
    		nodeValid = true;
    	}
    }
    else if (typeof(this.parentNode) == "string") { 
    	//first test if Windows group exists
    	if (!document.getElementById(this.parentNode)) {
        	this.parentGroup = document.createElementNS(svgNS,"g");
        	this.parentGroup.setAttributeNS(null,"id",this.parentNode);
        	document.documentElement.appendChild(this.parentGroup);
        	nodeValid = true;
   		}
   		else {
       		this.parentGroup = document.getElementById(this.parentNode);
       		nodeValid = true;
   		}
   	}
   	return nodeValid;
}

tabgroup.prototype.handleEvent = function(evt) {
	var tab = evt.target;
	var id = tab.getAttributeNS(null,"id");
	var idArray = id.split("__");
	var index = parseInt(idArray[1]);
	this.activateTabByIndex(index,false);
	//firefunction
	this.timer.setTimeout("fireFunction",this.timerMs);
}

tabgroup.prototype.fireFunction = function() {
	if (typeof(this.functionToCall) == "function") {
		this.functionToCall(this.id,this.tabTitles[this.activeTabindex],this.activeTabindex);
	}
	if (typeof(this.functionToCall) == "object") {
		this.functionToCall.tabActivated(this.id,this.tabTitles[this.activeTabindex],this.activeTabindex);
	}
	if (typeof(this.functionToCall) == undefined) {
		return;
	}
}

tabgroup.prototype.activateTabByIndex = function(tabindex,fireFunction) {
	if (tabindex >= this.tabTitles.length) {
		this.outOfBoundMessage(tabindex);
		tabindex = 0;
	}
	//set old active tab to inactive
	this.tabwindows[this.activeTabindex]["tab"].setAttributeNS(null,"fill",this.tabStyles["fill"]);
	if (this.hideContent) {
		this.tabwindows[this.activeTabindex]["content"].setAttributeNS(null,"display","none");
	}
	//set new index
	this.activeTabindex = tabindex;
	//activate new tab
	this.tabwindows[this.activeTabindex]["tab"].setAttributeNS(null,"fill",this.activetabBGColor);
	//reorder tabs
	this.tabGroup.appendChild(this.tabwindows[this.activeTabindex]["group"]);
	//set display
	if (this.hideContent) {
		this.tabwindows[this.activeTabindex]["content"].setAttributeNS(null,"display","inherit");
	}
	if (fireFunction) {
		this.timer.setTimeout("fireFunction",this.timerMs);
	}
}

tabgroup.prototype.activateTabByTitle = function(title,fireFunction) {
	var tabindex = -1;
	for (var i=0;i<this.tabTitles.length;i++) {
		if (this.tabTitles[i] == title) {
			tabindex = i;
			break;
		}
	}
	if (tabindex != -1) {
		this.activateTabByIndex(tabindex,fireFunction);
	}
	else {
		alert("Error ("+this.id+"): Could not find title '"+title+"' in array tabTitles!");
	}
}

//add content to this.tabwindows[tabindex]["content"]
tabgroup.prototype.addContent = function(node,tabindex,inheritDisplay) {
	if (tabindex >= this.tabTitles.length) {
		this.outOfBoundMessage(tabindex);
	}
	else {
		if (typeof(node) == "string") {
			node = document.getElementById(node);
		}
		if (node != undefined || node != "null") {
			if (inheritDisplay) {
				node.setAttributeNS(null,"display","inherit");
			}
			this.tabwindows[tabindex]["content"].appendChild(node);
		}
	}
}

//remove content from this.tabwindows[tabindex]["content"]
tabgroup.prototype.removeContent = function(node,tabindex) {
	var deletedNode = undefined;
	if (tabindex >= this.tabTitles.length) {
		this.outOfBoundMessage(tabindex);
	}
	else {
		if (typeof(node) == "string") {
			node = document.getElementById(node);
		}
		if (node != undefined || node != "null") {
			deletedNode = this.tabwindows[tabindex]["content"].removeChild(node);
		}
	}
	return deletedNode;
}

//move the tab, reference is upper left corner
tabgroup.prototype.moveTo = function(x,y) {
	this.transx = x;
	this.transy = y;
	this.tabGroup.setAttributeNS(null,"transform","translate("+this.transx+","+this.transy+")");
}

//resize tabgroup
tabgroup.prototype.resize = function(width,height) {
	this.width = width;
	this.height = height;
	//loop to change all tab sizes
	var currentX = this.x;
	for (var i=0;i<this.tabTitles.length;i++) {
		currentLeft = currentX;
		//get bbox
		var bbox = this.tabwindows[i]["tabTitle"].getBBox();
		currentX = Math.round(currentLeft + this.tabmargins * 2 + bbox.width);
		//start the path for tab windows
		var d = "M";
		//left corner of tab
		if (this.cornerLeft == "rect") {
			d += currentLeft+" "+this.y;
		}
		if (this.cornerLeft == "triangle") {
			d += currentLeft+" "+(this.y+this.tabmargins)+"L"+(currentLeft+this.tabmargins)+" "+this.y;
		}
		if (this.cornerLeft == "round") {
			d += currentLeft+" "+(this.y+this.tabmargins)+"a"+this.tabmargins+" "+this.tabmargins+" 0 0 1 "+this.tabmargins+" -"+this.tabmargins;
		}
		//right corner of tab
		if (this.cornerRight == "rect") {
			d += "L"+currentX+" "+this.y;
		}
		if (this.cornerRight == "triangle") {
			d += "L"+(currentX-this.tabmargins)+" "+this.y+"L"+currentX+" "+(this.y+this.tabmargins);
		}
		if (this.cornerRight == "round") {
			d += "L"+(currentX-this.tabmargins)+" "+this.y+"a"+this.tabmargins+" "+this.tabmargins+" 0 0 1 "+this.tabmargins+" "+this.tabmargins;
		}
		d += "L"+currentX+" "+(this.y+this.tabheight);
		//complete the path for tab window
		d += "L"+(this.x+this.width)+" "+(this.y+this.tabheight)+"L"+(this.x+this.width)+" "+(this.y+this.height);
		d += "L"+this.x+" "+(this.y+this.height);
		if (currentLeft == this.x) {
			d += "z";
		}
		else {
			d += "L"+this.x+" "+(this.y+this.tabheight)+"L"+currentLeft+" "+(this.y+this.tabheight)+"z";
		}
		//set modified path elements
		this.tabwindows[i]["tabbg"].setAttributeNS(null,"d",d);
		//increment currentX with the space between tabs
		currentX += this.spaceBetweenTabs;
	}
}

//deactivate a single tab
tabgroup.prototype.disableSingleTab = function(tabindex) {
	if (tabindex >= this.tabTitles.length) {
		this.outOfBoundMessage(tabindex);
	}
	else {
		this.tabwindows[tabindex]["activeStatus"] = false;
		if (!this.tabwindows[tabindex]["tabClone"]) {
			this.tabwindows[tabindex]["tabClone"] = this.tabwindows[tabindex]["tab"].cloneNode(false);
			this.tabwindows[tabindex]["tabClone"].removeEventListener("click",this,false);
			this.tabwindows[tabindex]["tabClone"].setAttributeNS(null,"fill-opacity",0.5);
			if (this.tabwindows[tabindex]["tabClone"].hasAttributeNS(null,"cursor")){
				this.tabwindows[tabindex]["tabClone"].removeAttributeNS(null,"cursor");
			}
			this.tabwindows[tabindex]["group"].appendChild(this.tabwindows[tabindex]["tabClone"]);
		}
		else {
			this.tabwindows[tabindex]["tabClone"].setAttributeNS(null,"display","inherit");
			this.tabwindows[tabindex]["group"].appendChild(this.tabwindows[tabindex]["tabClone"]);
		}
	}
}

//deactivate all tabs
tabgroup.prototype.disableAllTabs = function() {
	for (var i=0;i<this.tabTitles.length;i++) {
		this.disableSingleTab(i);
	}
}

//deactivate all tabs
tabgroup.prototype.enableAllTabs = function() {
	for (var i=0;i<this.tabTitles.length;i++) {
		this.enableSingleTab(i);
	}
}

//activate a single tab
tabgroup.prototype.enableSingleTab = function(tabindex) {
	if (tabindex >= this.tabTitles.length) {
		this.outOfBoundMessage(tabindex);
	}
	else {
		this.tabwindows[tabindex]["activeStatus"] = true;
		if (this.tabwindows[tabindex]["tabClone"]) {
			this.tabwindows[tabindex]["tabClone"].setAttributeNS(null,"display","none");
		}
	}
}

//out of bound error message
tabgroup.prototype.outOfBoundMessage = function(tabindex) {
	alert("Error ("+this.id+"): the 'tabindex' (value: "+tabindex+") is out of bounds!\nThe index nr is bigger than the number of tabs.");
} 
